﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NEmu;
using System.IO;

namespace Super64.Core.CPU.MIPS
{
    /// <summary>
    /// R3400i MIPS Core
    /// </summary>
    public abstract class MipsEngine : VMComponent
    {
        private RuntimeBitMode m_BitMode; // Register bit-width mode
        private ulong[] m_GeneralRegs; // General registers
        private double[] m_FloatRegs;  // Floating point registers
        private ulong m_PC;    // Program Counter
        private ulong m_MultHi; // Multiply/Divide Hi results
        private ulong m_MultLo; // Multiply/Divide Lo results
        private uint m_FCR0; // Implementation/Revision Information
        private uint m_FCR31; // Control/Status
        private bool m_LLReg; // Link/Load bit
        private Stream m_BootCode; // Bootcode

        Thread m_CycleThread;
        bool m_RequestCPUStop = false;
        EventWaitHandle m_CPUFinishWait;

        public MipsEngine(N64Machine parentMachine)
            : base(parentMachine)
        {
        }

        public abstract void Step();

        public void RunCycle()
        {
            while (true)
            {
                if (m_RequestCPUStop)
                    break;

                if (m_BootCode == null)
                    throw new VMException("No boot code loaded");

                Step();
            }

            m_CPUFinishWait.Set();
        }

        public virtual void Initialize()
        {
            m_BitMode = RuntimeBitMode.Mode32Bit;
            m_GeneralRegs = new ulong[32];
            m_FloatRegs = new double[32];
            m_RequestCPUStop = false;
            m_CPUFinishWait = new EventWaitHandle(false, EventResetMode.AutoReset);
            m_CycleThread = new Thread(new ThreadStart(RunCycle));
        }

        public virtual void Shutdown()
        {
        }

        public void LoadBootCode(Stream code)
        {
            m_BootCode = code;
            BootCode.Position = 0;
        }

        public void Run()
        {
            m_CycleThread.Start();
        }

        public void Stop()
        {
            m_RequestCPUStop = true;
            m_CPUFinishWait.WaitOne();
        }

        public void IncrementPC()
        {
            m_PC += 4;
        }

        public void SetBitMode(RuntimeBitMode mode)
        {
            m_BitMode = mode;
        }

        public ulong[] GeneralRegisters
        {
            get { return m_GeneralRegs; }
        }

        public double[] FloatRegisters
        {
            get { return m_FloatRegs; }
        }

        public RuntimeBitMode BitMode
        {
            get { return m_BitMode; }
        }

        public Stream BootCode
        {
            get { return m_BootCode; }
        }

        public ulong PC
        {
            get { return m_PC; }
        }
    }
}
